

use std::{collections::HashMap, fmt::{Debug, Display}, path::PathBuf, sync::Arc};

use clap::Parser;

use serde::{Deserialize, Serialize, de::DeserializeOwned};
use wtask_base::{
    error::{WError, WResult},
    export::{KillReceiver, KillSender, TokioRwLock, log_debug},
    tcp::{WTcpStream, stream_read_data, stream_read_raw, stream_write_data},
    timer::TimerList
};

use crate::{
    basic::{Empty, WPath, WRaw, WStatus, WStreamReader},
    file::{
        download::AppFileDownload,
        tree::{AppFileTree, FileTree},
        upload::AppFileUpload
    },
    home::AppHome,
    port::AppPort,
    room::AppRoom,
    shell::AppShell,
    task::{
        add::AppTaskAdd,
        archv::AppTaskArchv,
        check::AppTaskCheck,
        cmd::AppCmd,
        download::AppTaskDownload,
        init::AppTaskInit,
        list::AppTaskList,
        result::AppTaskResult,
        status::AppTaskStatus,
        submit::AppTaskSubmit,
        timer::{AppTaskTimerAdd, AppTaskTimerStatus},
        utils::meta::{CmdList, CmdRet, TaskList},
        zip::AppTaskZip
    }
};



// pub trait MsgFuncTraitFull: Fn(bool, String) + Send + Clone {}
// impl<T> MsgFuncTraitFull for T where T: Fn(bool, String) + Send + Clone {}

pub type StreamStatus = Arc<TokioRwLock<(
    KillReceiver<bool>,
    HashMap<u32, KillSender<bool>>,
    HashMap<u32, u32>
)>>;



pub trait AppTrait
where
    Self: Sized + Send + Clone + Debug + Into<App>,
    Self::Res: DeserializeOwned + Serialize + Into<AppRes> + Send + Sync + Display,
{
    type Res;
    type Status;

    /// client发送数据前调用，如果返回None，则不用发送数据，请求中断
    fn client_handle_before<M: FnMut(bool, String) + Send + Sync>(self, _msg_func: M, status: Option<Self::Status>) -> WResult<(Option<Self>, Option<Self::Status>)> {
        Ok((Some(self), status))
    }

    // 服务器默认的一次请求，返回数据
    fn server_handle_single(self) -> impl Future<Output = WResult<Self::Res>> + Send {
        async move {
            Err(WError::Stop)
        }
    }

    // 服务器处理，默认是读取完一次请求后，进行处理
    fn server_handle(self, _server_token: &str, session_token: &str, stream_session: &WTcpStream, _stream_status: StreamStatus) -> impl Future<Output = WResult<()>> + Send {
        async move {
            match self.server_handle_single().await {
                Ok(app_res) => {
                    stream_write_data::<String>(
                        &"normal".to_owned(),
                        stream_session.1.as_ref(),
                        session_token,
                    ).await?;
                    stream_write_data(
                        &app_res,
                        stream_session.1.as_ref(),
                        session_token,
                    ).await?;
                },
                Err(e) => {
                    stream_write_data(
                        &e.to_string(),
                        stream_session.1.as_ref(),
                        session_token,
                    ).await?;
                    log_debug!("Machine session app res error: {} {:?}", session_token, e);
                }
            };
        Ok(())
        }
    }

    /// msg_func: is_err/msg
    fn client_handle<M: FnMut(bool, String) + Send + Sync>(self, stream: &WTcpStream, key: &str, mut msg_func: M, _status: Option<Self::Status>) -> impl Future<Output = WResult<Self::Res>> + Send {
        async move {
            let msg = stream_read_data::<String>(
                stream.0.as_ref(),
                key,
            ).await?;
            if msg != "normal" {
                msg_func(true, msg);
                return Err(WError::Stop);
            }
            let res = stream_read_data::<Self::Res>(
                stream.0.as_ref(),
                key,
            ).await?;
            msg_func(false, format!("{}", res));
            Ok(res)
        }
    }

    fn client_wait_log<V: DeserializeOwned + Send, M: FnMut(bool, String) + Send + Sync>(stream: &WTcpStream, key: &str, mut msg_func: M) -> impl Future<Output = WResult<V>> + Send {
        async move {
            let res_value = stream_read_data::<V>(
                stream.0.as_ref(),
                key,
            ).await?;
            while let Ok(res_log_temp) = stream_read_raw(stream.0.as_ref(), key).await {
                msg_func(false, String::from_utf8_lossy(&res_log_temp).to_string());
            }
            Ok(res_value)
        }
    }
}





macro_rules! app_enum {
    ("client_handle_before", $val:expr, $fn_name:ident, $arg1:expr, $arg2:expr; $($member:ident),* $(,)?) => {
        match $val {
            $(
                Self::$member(v) => {
                    if let Some(r) = v.$fn_name($arg1, $arg2)?.0 {
                        Some(Self::$member(r))
                    } else {
                        None
                    }
                },
            )*
        }
    };
    
    ("server_handle", $val:expr, $fn_name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr; $($member:ident),* $(,)?) => {
        match $val {
            $(
                Self::$member(v) => v.$fn_name($arg1, $arg2, $arg3, $arg4).await,
            )*
        }
    };
    
    ("client_handle", $val:expr, $fn_name:ident, $arg1:expr, $arg2:expr, $arg3:expr, $arg4:expr; $($member:ident),* $(,)?) => {
        match $val {
            $(
                Self::$member(v) => v.$fn_name($arg1, $arg2, $arg3, $arg4).await?.into(),
            )*
        }
    };

    ("appres_from"; $($variant:ident($type:ty)),* $(,)?) => {
        #[derive(Debug, Serialize, Deserialize)]
        pub enum AppRes {
            $(
                $variant($type),
            )*
        }
        $(
            impl From<$type> for AppRes {
                fn from(value: $type) -> Self {
                    Self::$variant(value)
                }
            }
        )*
    };
    
    ("app_from"; $($variant:ident($type:ty, $doc:expr)),* $(,)?) => {
        #[derive(Debug, Clone, Serialize, Deserialize, Parser)]
        pub enum App {
            $(
                #[doc = $doc]
                $variant($type),
            )*
        }
        $(
            impl From<$type> for App {
                fn from(value: $type) -> Self {
                    Self::$variant(value)
                }
            }
        )*
        impl App {
            pub fn client_handle_before<M: FnMut(bool, String) + Send + Sync>(self, msg_func: M) -> WResult<Option<Self>> {
                let r = app_enum!(
                    "client_handle_before", self, client_handle_before, msg_func, None;
                    $($variant,)*
                );
                Ok(r)
            }
            
            pub async fn server_handle(self, server_token: &str, session_token: &str, stream_session: &WTcpStream, stream_status: StreamStatus) -> WResult<()> {
                app_enum!(
                    "server_handle", self, server_handle, server_token, session_token, stream_session, stream_status;
                    $($variant,)*
                )
            }

            pub async fn client_handle<M: FnMut(bool, String) + Send + Sync>(self, stream: &WTcpStream, key: &str, msg_func: M) -> WResult<AppRes> {
                let r = app_enum!(
                    "client_handle", self, client_handle, stream, key, msg_func, None;
                    $($variant,)*
                );
                Ok(r)
            }
        }
    };
}


app_enum!{
    "appres_from";
    Null(Empty),
    Msg(String),
    Path(WPath),
    Status(WStatus),
    Raw(WRaw),
    Stream(WStreamReader),
    FileTree(FileTree),
    CmdRet(CmdRet),
    TaskList(TaskList),
    CmdList(CmdList),
    TimerList(TimerList<PathBuf>),
}

app_enum!{
    "app_from";
    Home(AppHome, "获取home路径"),
    FileTree(AppFileTree, "获取文件夹的所有文件树"),
    FileDownload(AppFileDownload, "文件下载"),
    FileUpload(AppFileUpload, "文件上传"),
    Cmd(AppCmd, "执行cmd命令"),
    TaskInit(AppTaskInit, "在本地文件夹生成任务预定义的模板"),
    TaskCheck(AppTaskCheck, "在本地文件夹检查是否符合预定义任务要求"),
    TaskZip(AppTaskZip, "将本地任务文件夹压缩"),
    TaskAdd(AppTaskAdd, "增加预定义任务"),
    TaskList(AppTaskList, "获取预定义任务列表"),
    TaskStatus(AppTaskStatus, "更改预定义任务状态"),
    TaskDownload(AppTaskDownload, "预定义任务下载备份"),
    Task(AppTaskSubmit, "任务提交运行"),
    TaskResult(AppTaskResult, "任务运行结果"),
    TaskArchv(AppTaskArchv, "存档或删除任务结果"),
    TimerAdd(AppTaskTimerAdd, "定时任务增加"),
    TimerStatus(AppTaskTimerStatus, "定时任务状态管理"),
    Port(AppPort, "端口转发"),
    Shell(AppShell, "远程shell"),
    Room(AppRoom, "广播室"),
}


